﻿#include "pch.h"
#include "sbhs.h"
#include "sbctn.h"
#include "kh_compiler.h"
#include "kh_code.h"
#include "extend/sberror.h"
#include "extend/sbmnehs.h"

//////////////////////////////////////////////////////////////////////////
// 기본

// 빨갱 문자
K_SLICE_DECL(khRsSlice, kRedStr*);
K_MUKUM_DECL(khRsMukum, const char*, kRedStr*);
K_MUKUM_PTR_BOTH_NONE(khRsMukum);
K_MUKUM_PTR_HASHER_CHAR_PTR(khRsMukum);

// 빨갱 와이드
K_SLICE_DECL(khRwSlice, kRedWcs*);
K_MUKUM_DECL(khRwMukum, const kwchar*, kRedWcs*);
K_MUKUM_PTR_BOTH_NONE(khRwMukum);
K_MUKUM_PTR_HASHER_WCHAR_PTR(khRwMukum);

// 빨갱 테이블
K_SLICE_DECL(khRtSlice, kRedTbl*);
K_ARR_DECL(khTblAnyCb, kanycb);

// 빨갱 함수
K_SLICE_DECL(htRfSlice, kRedFn*);
K_CTNR_DECL(khObjCtn, kBase*);

// 정적 데떠, 초기화는 한참 아래
static struct kRedImpl
{
	// 빨깽 문자
	const char*			rs_mesg;
	khRsSlice			rs_cache;
	khRsMukum			rs_stts;

	// 빨갱 와이드
	const kwchar*		rw_mesg;
	khRwSlice			rw_cache;
	khRwMukum			rw_stts;

	// 빨강 테이블
	khRtSlice			rt_cache;

	// 빨강 함수
	kuintptr			rf_index;
	htRfSlice			rf_cache;
} kh_red_impl =
{
	"<cache>", {0, }, {0, },
	L"<cache>", {0, }, {0, },
	{0, },
	0, {0, }
};

//////////////////////////////////////////////////////////////////////////
// 빨갱 문자

//
static int _kh_rs_op_sig(kVar* ops);
static int _kh_rs_op_add(kVar* ops);
static int _kh_rs_op_blt(kVar* ops);
static int _kh_rs_op_ble(kVar* ops);
static int _kh_rs_op_beq(kVar* ops);
static int _kh_rs_op_bne(kVar* ops);
static int _kh_rs_op_stv(kVar* ops);
static int _kh_rs_op_ldv(kVar* ops);
static int _kh_rs_op_new(kVar* ret, kint arity, kVar* args);
static const char* _kh_rs_op_tos(const kVar* v, char* buf, kint size, kint* out);

static kint _kh_rs_mt_left(kScv* s);
static kint _kh_rs_mt_right(kScv* s);
static kint _kh_rs_mt_mid(kScv* s);
static kint _kh_rs_mt_length(kScv* s);
static kint _kh_rs_mt_is_have(kScv* s);
static kint _kh_rs_mt_is_empty(kScv* s);
static kint _kh_rs_mt_compare(kScv* s);
static kint _kh_rs_mt_to_lower(kScv* s);
static kint _kh_rs_mt_to_upper(kScv* s);
static kint _kh_rs_mt_to_int(kScv* s);
static kint _kh_rs_mt_to_long(kScv* s);
static kint _kh_rs_mt_to_float(kScv* s);
static kint _kh_rs_mt_to_double(kScv* s);
static kint _kh_rs_mt_replace(kScv* s);
static kint _kh_rs_mt_split(kScv* s);

// 제거용
static void _k_redstr_finalize(kRedStr* self)
{
	self->stt = FALSE;
	k_base_force_unload((kBase*)self);
}

//
kType k_redstr_type(void)
{
	return KTYPE_STR;
}

//
static void _k_redstr_type_init(kType type)
{
	static struct khOpTable optbl =
	{
		NULL,

		NULL,
		NULL,
		_kh_rs_op_sig,
		NULL,
		NULL,
		NULL,

		NULL,
		NULL,
		NULL,
		_kh_rs_op_add,
		NULL,
		NULL,
		NULL,
		_kh_rs_op_blt,
		_kh_rs_op_ble,
		_kh_rs_op_beq,
		_kh_rs_op_bne,
		NULL,
		NULL,
		NULL,

		_kh_rs_op_stv,
		_kh_rs_op_ldv,

		_kh_rs_op_new,
		_kh_rs_op_tos,
	};
#define MAPMTD(name,func)	KHPARAM_FUNC_REG_SIMPLE(name, _kh_rs_mt_##func)
	static const struct khParamFuncReg fns[] =
	{
		MAPMTD("Left", left),
		MAPMTD("Right", right),
		MAPMTD("Mid", mid),
		MAPMTD("Length", length),
		MAPMTD("IsHave", is_have),
		MAPMTD("IsEmpty", is_empty),
		MAPMTD("Compare", compare),
		MAPMTD("ToLower", to_lower),
		MAPMTD("ToUpper", to_upper),
		MAPMTD("ToInt", to_int),
		MAPMTD("ToLong", to_long),
		MAPMTD("ToFloat", to_float),
		MAPMTD("ToDouble", to_double),
		MAPMTD("Replace", replace),
		MAPMTD("Split", split),
	};
#undef MAPMTD

	k_type_set_optable(type, &optbl);
	k_type_bind_funcs(type, 1, K_COUNTOF(fns), fns);
}

//
static kpointer _k_redstr_disp(kBase* ptr)
{
	kRedStr* self = (kRedStr*)ptr;
	kcham ret;

	if (self->stt)
	{
		// 정적이면 다시 참조 소생 후 안지움
		k_base_force_revive((kBase*)self);
		return NULL;
	}

	k_h_lock();;

	k_slice_ptr_push(khRsSlice, &kh_red_impl.rs_cache, self, &ret);

	if (ret)
	{
		// 캐시에 넣을 수 있으면 정리 안하고 그냥 넣기
		if (self->sym)
		{
			self->sym = K_MAX_UINT;
			self->data = (char*)kh_red_impl.rs_mesg;
		}
		else
		{
#if _SB_DEBUG_
			if (self->len > 8)
				memcpy(self->data, kh_red_impl.rs_mesg, 8);
			else
				memset(self->data, '@', self->len);

			self->len = 0;
#endif
		}

		ret = FALSE;
	}
	else
	{
		// 진짜 지움
		if (self->sym == 0)
			k_delete(self->data);

		ret = TRUE;
	}

	k_h_unlock();

	return ret ? self : NULL;
}

// 설정
kcham k_redstr_set_str(kRedStr* self, const char* str, kint len)
{
	k_return_value_if_fail(self->data != str, FALSE);

	if (!str)
	{
		if (self->sym == 0)
			k_delete(self->data);

		self->sym = 0;
		self->len = 0;
		self->data = NULL;
	}
	else
	{
		kuint sym = k_symlet(str, &str);

		if (sym != 0)
		{
			if (self->sym == 0)
				k_delete(self->data);

			self->sym = sym;
			self->len = (kint)k_symunlen(sym);
			self->data = (char*)str;
		}
		else
		{
			if (len <= 0)
				len = (kint)k_strlen(str);

			self->data = (self->sym == 0) ? k_renew(self->data, len + 1, char) : k_new(len + 1, char);
			k_strncpy(self->data, str, len);
			self->len = len;
			self->sym = 0;
		}
	}

	return TRUE;
}

// 심볼
kcham k_redstr_set_sym(kRedStr* self, kuint sym)
{
	if (self->sym == 0)
		k_delete(self->data);

	if (sym == 0)
	{
		self->sym = 0;
		self->len = 0;
		self->data = NULL;

		return FALSE;
	}
	else
	{
		ksize_t len;
		k_symunvalue(sym, (const char**)&self->data, &len);

		self->len = (kint)len;
		self->sym = sym;

		return TRUE;
	}
}

// 데이터
const char* k_redstr_get_data(kRedStr* self)
{
	return self->data;
}

// 길이
kint k_redstr_get_len(kRedStr* self)
{
	return self->len;
}

// 심볼
kuint k_redstr_get_sym(kRedStr* self)
{
	return self->sym;
}

// 심볼로 있는 문자열인가만 검사, 바꾸지 않음
kuint k_redstr_try_sym(kRedStr* self)
{
	k_return_value_if_fail(self->sym == 0, self->sym);
	k_return_value_if_fail(self->data, 0);

	return k_symtry(self->data);
}

// 심볼화
kuint k_redstr_build_sym(kRedStr* self, kcham istry)
{
	k_return_value_if_fail(self->sym == 0, self->sym);
	k_return_value_if_fail(self->data, 0);

	if (istry)
	{
		kuint sym = k_symtry(self->data);
		k_return_value_if_fail(sym > 0, 0);

		k_delete(self->data);
		self->data = (char*)k_symunstr(sym);
		self->sym = sym;
	}
	else
	{
		char* save = self->data;
		self->data = (char*)k_symbothflx(save, &self->sym);
		self->len = (kint)k_symunlen(self->sym);
		k_delete(save);
	}

	return self->sym;
}

// 테스트 -> 심볼일 경우 심볼이 아닌 문자열로 바꿈
kcham k_redstr_build_data(kRedStr* self)
{
	const char* psz;

	k_return_value_if_fail(self->sym != 0, FALSE);

	psz = self->data;
	self->sym = 0;
	self->data = k_new(self->len + 1, char);
	k_strcpy(self->data, psz);

	return TRUE;
}

// 찾기
kRedStr* k_redstr_get_static(const char* str)
{
	kRedStr* self;

	k_return_value_if_fail(str, NULL);

	k_h_lock();;
	k_mukum_ptr_get(khRsMukum, &kh_red_impl.rs_stts, str, &self);
	k_h_unlock();

	return self;
}

// 캐시 크기
void k_redstr_set_cache_size(kint count)
{
	k_return_if_fail(count >= 0);
	k_return_if_fail(count > 16);

	k_h_lock();;
	k_slice_expand(khRsSlice, &kh_red_impl.rs_cache, (ksize_t)count);
	k_h_unlock();
}

// 캐시 크기
kint k_redstr_get_cache_size(void)
{
	return (kint)k_slice_maximum(&kh_red_impl.rs_cache);
}

// 현재 캐시
kint k_redstr_get_cache_count(void)
{
	return (kint)k_slice_count(&kh_red_impl.rs_cache);
}

// 만들기
kRedStr* k_redstr_new(const char* init, kint len)
{
	kRedStr* self;

	k_h_lock();;
	k_slice_ptr_pop_value(khRsSlice, &kh_red_impl.rs_cache, &self);
	k_h_unlock();

	if (!self)
		self = (kRedStr*)k_crbs_new(KTYPE_STR);
	else
		k_base_force_revive((kBase*)self);

	k_redstr_set_str(self, init, len);

	return self;
}

// 만들기, 심볼
kRedStr* k_redstr_new_sym(kuint sym)
{
	kRedStr* self;

	k_h_lock();;
	k_slice_ptr_pop_value(khRsSlice, &kh_red_impl.rs_cache, &self);
	k_h_unlock();

	if (!self)
		self = (kRedStr*)k_crbs_new(KTYPE_STR);
	else
		k_base_force_revive((kBase*)self);

	k_redstr_set_sym(self, sym);

	return self;
}

// 만들기, 정적
kRedStr* k_redstr_new_static(const char* init, kint len)
{
	kRedStr* self = k_redstr_get_static(init);

	if (!self)
	{
		k_h_lock();;
		k_slice_ptr_pop_value(khRsSlice, &kh_red_impl.rs_cache, &self);
		k_h_unlock();

		if (!self)
			self = (kRedStr*)k_crbs_new(KTYPE_STR);
		else
			k_base_force_revive((kBase*)self);

		self->stt = TRUE;
		k_redstr_set_str(self, init, len);

		k_h_lock();;
		k_mukum_ptr_add(khRsMukum, &kh_red_impl.rs_stts, self->data, self);
		k_h_unlock();
	}

	return (kRedStr*)k_base_load((kBase*)self);
}

// 만들기, 복사
kRedStr* k_redstr_new_copy(kRedStr* self)
{
	kRedStr* dest;

	k_h_lock();;
	k_slice_ptr_pop_value(khRsSlice, &kh_red_impl.rs_cache, &dest);
	k_h_unlock();

	if (!dest)
		dest = (kRedStr*)k_crbs_new(KTYPE_STR);
	else
		k_base_force_revive((kBase*)dest);

	if (self->sym)
	{
		if (dest->sym == 0)
			k_delete(dest->data);

		dest->data = self->data;
		dest->len = self->len;
		dest->sym = self->sym;
	}
	else
	{
		dest->data = (dest->sym == 0) ? k_renew(dest->data, self->len + 1, char) : k_new(self->len + 1, char);
		k_strcpy(dest->data, self->data);
		dest->len = self->len;
		dest->sym = 0;
	}

	return dest;
}

//
static const char* _kh_rs_op_value(kVar* v, kVar* save, kBstr64* bs)
{
	k_var_agn(save, v);
	return k_var_tos(v, bs->data, 64, &bs->len);
}

//
static int _kh_rs_op_sig(kVar* ops)
{
	k_var_unload(&ops[0]);
	k_var_set_bool(&ops[0], FALSE);

	return 0;
}

//
static int _kh_rs_op_add(kVar* ops)
{
	kRedStr* self;
	kVar save[2];
	kBstr64 bs[2];
	const char* psz[2];
	char* sz;
	ksize_t len;

	psz[0] = _kh_rs_op_value(&ops[0], &save[0], &bs[0]);
	psz[1] = _kh_rs_op_value(&ops[1], &save[1], &bs[1]);

	len = bs[0].len + bs[1].len;
	sz = k_new(len + 1, char);

	memcpy(sz, psz[0], bs[0].len);
	memcpy(sz + bs[0].len, psz[1], bs[1].len);
	sz[len] = '\0';

	self = k_redstr_new(sz, (kint)len);
	k_var_agn_type(&ops[0], KTYPE_STR, (kBase*)self);

	k_delete(sz);
	k_var_unload(&save[0]);
	k_var_unload(&save[1]);

	return 0;
}

//
static int _kh_rs_op_blt(kVar* ops)
{
	kVar save[2];
	const char* psz[2];
	kBstr64 bs[2];

	psz[0] = _kh_rs_op_value(&ops[0], &save[0], &bs[0]);
	psz[1] = _kh_rs_op_value(&ops[1], &save[1], &bs[1]);

	k_var_set_bool(&ops[0], k_strcmp(psz[0], psz[1]) < 0);

	k_var_unload(&save[0]);
	k_var_unload(&save[1]);

	return 0;
}

//
static int _kh_rs_op_ble(kVar* ops)
{
	kVar save[2];
	const char* psz[2];
	kBstr64 bs[2];

	psz[0] = _kh_rs_op_value(&ops[0], &save[0], &bs[0]);
	psz[1] = _kh_rs_op_value(&ops[1], &save[1], &bs[1]);

	k_var_set_bool(&ops[0], k_strcmp(psz[0], psz[1]) > 0);

	k_var_unload(&save[0]);
	k_var_unload(&save[1]);

	return 0;
}

//
static int _kh_rs_op_beq(kVar* ops)
{
	kVar save[2];
	const char* psz[2];
	kBstr64 bs[2];

	psz[0] = _kh_rs_op_value(&ops[0], &save[0], &bs[0]);
	psz[1] = _kh_rs_op_value(&ops[1], &save[1], &bs[1]);

	k_var_set_bool(&ops[0], k_strcmp(psz[0], psz[1]) == 0);

	k_var_unload(&save[0]);
	k_var_unload(&save[1]);

	return 0;
}

//
static int _kh_rs_op_bne(kVar* ops)
{
	kVar save[2];
	const char* psz[2];
	kBstr64 bs[2];

	psz[0] = _kh_rs_op_value(&ops[0], &save[0], &bs[0]);
	psz[1] = _kh_rs_op_value(&ops[1], &save[1], &bs[1]);

	k_var_set_bool(&ops[0], k_strcmp(psz[0], psz[1]) != 0);

	k_var_unload(&save[0]);
	k_var_unload(&save[1]);

	return 0;
}

//
static int _kh_rs_op_stv(kVar* ops)
{
	kRedStr* self = (kRedStr*)k_var_unsafe_base(&ops[0]);
	int ret, ch, idx;

	if (!k_var_safe_int(&ops[1], &idx))
	{
		// 기본
		ret = kh_op_supp_stv(KTYPE_STR, &ops[0], &ops[1], &ops[2]);
	}
	else if (k_var_safe_int(&ops[2], &ch))
	{
		// 넣을값이 정수고, 범위 안에 있으면
		if (ch >= 0 && ch <= 255 && (ksize_t)idx < (ksize_t)self->len)
		{
			// 변경
			k_redstr_build_data(self);
			self->data[idx] = (char)ch;
		}

		ret = 0;
	}
	else
	{
		k_var_unload(&ops[2]);
		// ops[1]은 이미 int임을 확인했으므로 패스
		ret = 0;
	}

	k_base_unload((kBase*)self);

	return ret;
}

//
static int _kh_rs_op_ldv(kVar* ops)
{
	kRedStr* self = (kRedStr*)k_var_unsafe_base(&ops[0]);
	int ret, idx;

	if (!k_var_safe_int(&ops[1], &idx))
	{
		// 기본
		ret = kh_op_supp_ldv(KTYPE_STR, &ops[0], &ops[1], &ops[0]);
	}
	else
	{
		if ((kuint)idx >= (kuint)self->len)
			k_var_zero(&ops[0]);
		else
			k_var_set_int(&ops[0], self->data[idx]);

		ret = 0;
	}

	k_base_unload((kBase*)self);

	return ret;
}

//
static int _kh_rs_op_new(kVar* ret, kint arity, kVar* args)
{
	if (arity != 1)
		k_var_zero(ret);
	else
	{
		char buf[64];
		kint len;
		const char* psz = k_var_tos(&args[0], buf, 64, &len);
		k_var_agn_type(ret, KTYPE_STR, (kBase*)k_redstr_new(psz, len));
	}

	return 0;
}

//
static const char* _kh_rs_op_tos(const kVar* v, char* buf, kint size, kint* out)
{
	kRedStr* self = (kRedStr*)k_var_unsafe_base(v);

	if (out) *out = self->len;

	return self->data;
}

// string left(int cnt)
static kint _kh_rs_mt_left(kScv* s)
{
	kRedStr* self;
	kint cnt;
	char* psz;

	if (!k_scv_self_type(s, KTYPE_STR, (kBase**)&self))
		return -1;

	if (!k_scv_arg_int(s, 0, &cnt))
		k_scv_push_self(s, NULL);
	else
	{
		cnt = K_CLAMP(cnt, 0, self->len);
		psz = k_new(cnt + 1, char);
		k_strncpy(psz, self->data, cnt);
		k_scv_push_str(s, psz, cnt);
		k_delete(psz);
	}

	return 0;
}

// string right(int cnt)
static kint _kh_rs_mt_right(kScv* s)
{
	kRedStr* self;
	kint cnt;
	char* psz;

	if (!k_scv_self_type(s, KTYPE_STR, (kBase**)&self))
		return -1;

	if (!k_scv_arg_int(s, 0, &cnt))
		k_scv_push_self(s, NULL);
	else
	{
		cnt = K_CLAMP(cnt, 0, self->len);
		psz = k_new(cnt + 1, char);
		k_strncpy(psz, self->data + self->len - cnt, cnt);
		k_scv_push_str(s, psz, cnt);
		k_delete(psz);
	}

	return 0;
}

// string mid(int index, [int count])
static kint _kh_rs_mt_mid(kScv* s)
{
	kRedStr* self;
	kint idx, cnt;
	char* psz;

	if (!k_scv_self_type(s, KTYPE_STR, (kBase**)&self))
		return -1;

	if (!k_scv_arg_int(s, 0, &idx))
		k_scv_push_self(s, NULL);
	else
	{
		if (idx >= self->len)
			k_scv_push_null(s);
		else
		{
			if (!k_scv_arg_int(s, 1, &cnt))
				cnt = 0;

			if ((idx + cnt) >= self->len)
				cnt = self->len - idx;

			psz = k_new(cnt + 1, char);
			k_strncpy(psz, self->data + idx, cnt);
			k_scv_push_str(s, psz, cnt);
			k_delete(psz);
		}
	}

	return 0;
}

// int length()
static kint _kh_rs_mt_length(kScv* s)
{
	kRedStr* self;

	if (!k_scv_self_type(s, KTYPE_STR, (kBase**)&self))
		return -1;

	if (k_scv_arity(s) > 0)
		k_scv_push_null(s);
	else
		k_scv_push_int(s, self->len);

	return 0;
}

// bool ishave()
static kint _kh_rs_mt_is_have(kScv* s)
{
	kRedStr* self;

	if (!k_scv_self_type(s, KTYPE_STR, (kBase**)&self))
		return -1;

	if (k_scv_arity(s) > 0)
		k_scv_push_null(s);
	else
		k_scv_push_bool(s, self->len != 0);

	return 0;
}

// bool isempty()
static kint _kh_rs_mt_is_empty(kScv* s)
{
	kRedStr* self;

	if (!k_scv_self_type(s, KTYPE_STR, (kBase**)&self))
		return -1;

	if (k_scv_arity(s) > 0)
		k_scv_push_null(s);
	else
		k_scv_push_bool(s, self->len == 0);

	return 0;
}

// int compare(var dest[, bool igcase])
static kint _kh_rs_mt_compare(kScv* s)
{
	kRedStr* self;
	const char* psz;
	kbool igcase;

	if (!k_scv_self_type(s, KTYPE_STR, (kBase**)&self))
		return -1;

	if (!k_scv_arg_str(s, 0, &psz, NULL))
		k_scv_push_null(s);
	else
	{
		if (!k_scv_arg_bool(s, 1, &igcase))
			igcase = FALSE;

		k_scv_push_int(s, igcase ? k_stricmp(self->data, psz) : k_strcmp(self->data, psz));
	}

	return 0;
}

// string tolower()
static kint _kh_rs_mt_to_lower(kScv* s)
{
	kRedStr* self;
	char* psz;

	if (!k_scv_self_type(s, KTYPE_STR, (kBase**)&self))
		return -1;

	if (k_scv_arity(s) > 0)
		k_scv_push_null(s);
	else
	{
		psz = k_new(self->len, char);
		k_strcpy(psz, self->data);
		k_strlwr(psz);
		k_scv_push_str(s, psz, self->len);
		k_delete(psz);
	}

	return 0;
}

// string toupper()
static kint _kh_rs_mt_to_upper(kScv* s)
{
	kRedStr* self;
	char* psz;

	if (!k_scv_self_type(s, KTYPE_STR, (kBase**)&self))
		return -1;

	if (k_scv_arity(s) > 0)
		k_scv_push_null(s);
	else
	{
		psz = k_new(self->len, char);
		k_strcpy(psz, self->data);
		k_strupr(psz);
		k_scv_push_str(s, psz, self->len);
		k_delete(psz);
	}

	return 0;
}

// int toint([int base])
static kint _kh_rs_mt_to_int(kScv* s)
{
	kRedStr* self;
	kint base;

	if (!k_scv_self_type(s, KTYPE_STR, (kBase**)&self))
		return -1;

	if (!k_scv_arg_int(s, 0, &base))
		base = 10;

	k_scv_push_int(s, base == 10 ? k_strtoi(self->data) : (kint)k_strtoi_base(self->data, base));

	return 0;
}

// int tolong([int base])
static kint _kh_rs_mt_to_long(kScv* s)
{
	kRedStr* self;
	kint base;

	if (!k_scv_self_type(s, KTYPE_STR, (kBase**)&self))
		return -1;

	if (!k_scv_arg_int(s, 0, &base))
		base = 10;

	k_scv_push_long(s, base == 10 ? k_strtol(self->data) : (klong)k_strtol_base(self->data, base));

	return 0;
}

// int tofloat()
static kint _kh_rs_mt_to_float(kScv* s)
{
	kRedStr* self;

	if (!k_scv_self_type(s, KTYPE_STR, (kBase**)&self))
		return -1;

	if (k_scv_arity(s) > 0)
		k_scv_push_null(s);
	else
		k_scv_push_float(s, k_atof(self->data));

	return 0;
}

// int todouble()
static kint _kh_rs_mt_to_double(kScv* s)
{
	kRedStr* self;

	if (!k_scv_self_type(s, KTYPE_STR, (kBase**)&self))
		return -1;

	if (k_scv_arity(s) > 0)
		k_scv_push_null(s);
	else
		k_scv_push_double(s, k_atod(self->data));

	return 0;
}

// string replace(string from_list, int to)
static kint _kh_rs_mt_replace(kScv* s)
{
	kRedStr* self;
	const char* rep;
	char* psz;
	char* at;
	kint ch;

	if (!k_scv_self_type(s, KTYPE_STR, (kBase**)&self))
		return -1;

	if (!k_scv_arg_str(s, 0, &rep, NULL) ||
		!k_scv_arg_int(s, 1, &ch) || ch < 0 || ch > 255)
		k_scv_push_null(s);
	else if (k_strchr(rep, ch))
	{
		// 바꿀 대상이 안에 있으면 오류지...
		k_scv_push_null(s);
	}
	else
	{
		psz = k_new(self->len + 1, char);
		k_strcpy(psz, self->data);

		for (at = psz;;)
		{
			at = k_strbrk(at, rep);

			if (!at)
				break;
			else
				*at = (char)ch;
		}

		k_scv_push_str(s, psz, self->len);
		k_delete(psz);
	}

	return 0;
}

// table split(char ch[, bool remove_empty])
static kint _kh_rs_mt_split(kScv* s)
{
	kRedStr* self;
	kVar* sv;

	const char* spl;
	char tmpspl[2];
	kbool rememp = FALSE;

	kRedTbl* tbl = k_redtbl_new();
	kVar kk, vv;
	kint n, len;
	const char* tok;
	char* ctx;

	if (!k_scv_self_type(s, KTYPE_STR, (kBase**)&self))
		return -1;

	sv = k_scv_arg(s, 0);

	if (k_var_is_int(sv))
	{
		tmpspl[0] = (char)k_var_unsafe_int(sv);
		tmpspl[1] = '\0';
		spl = tmpspl;
	}
	else
	{
		spl = k_var_unsafe_str(sv, NULL);

		if (!spl)
		{
			k_scv_push_null(s);
			return 0;
		}
	}

	if (k_scv_arity(s) > 1)
	{
		sv = k_scv_arg(s, 1);
		k_var_safe_bool(sv, &rememp);
	}

	for (n = 0, tok = k_strtok(self->data, spl, &ctx); tok; tok = k_strtok(NULL, spl, &ctx))
	{
		len = (kint)k_strlen(tok);
		if (rememp && len == 0)
			continue;

		k_var_set_int(&kk, n++);
		k_var_set_str(&vv, tok, len);

		k_redtbl_set(tbl, &kk, &vv);
	}

	k_scv_push_type(s, KTYPE_TBL, (kBase*)tbl);

	k_base_unload((kBase*)tbl);

	return 0;
}


//////////////////////////////////////////////////////////////////////////
// 빨강 유니

//
static int _kh_rw_op_sig(kVar* ops);
static int _kh_rw_op_add(kVar* ops);
static int _kh_rw_op_blt(kVar* ops);
static int _kh_rw_op_ble(kVar* ops);
static int _kh_rw_op_beq(kVar* ops);
static int _kh_rw_op_bne(kVar* ops);
static int _kh_rw_op_stv(kVar* ops);
static int _kh_rw_op_ldv(kVar* ops);
static int _kh_rw_op_new(kVar* ret, kint arity, kVar* args);
static const char* _kh_rw_op_tos(const kVar* v, char* buf, kint size, kint* out);

static kint _kh_rw_mt_left(kScv* s);
static kint _kh_rw_mt_right(kScv* s);
static kint _kh_rw_mt_mid(kScv* s);
static kint _kh_rw_mt_length(kScv* s);
static kint _kh_rw_mt_is_have(kScv* s);
static kint _kh_rw_mt_is_empty(kScv* s);
static kint _kh_rw_mt_compare(kScv* s);
static kint _kh_rw_mt_to_lower(kScv* s);
static kint _kh_rw_mt_to_upper(kScv* s);
static kint _kh_rw_mt_to_int(kScv* s);
static kint _kh_rw_mt_to_long(kScv* s);
static kint _kh_rw_mt_to_float(kScv* s);
static kint _kh_rw_mt_to_double(kScv* s);
static kint _kh_rw_mt_replace(kScv* s);
static kint _kh_rw_mt_split(kScv* s);

// 제거용
static void _k_redwcs_finalize(kRedWcs* self)
{
	self->stt = FALSE;
	k_base_force_unload((kBase*)self);
}

//
kType k_redwcs_type(void)
{
	return KTYPE_WCS;
}

//
static void _k_redwcs_type_init(kType type)
{
	static struct khOpTable optbl =
	{
		NULL,

		NULL,
		NULL,
		_kh_rw_op_sig,
		NULL,
		NULL,
		NULL,

		NULL,
		NULL,
		NULL,
		_kh_rw_op_add,
		NULL,
		NULL,
		NULL,
		_kh_rw_op_blt,
		_kh_rw_op_ble,
		_kh_rw_op_beq,
		_kh_rw_op_bne,
		NULL,
		NULL,
		NULL,

		_kh_rw_op_stv,
		_kh_rw_op_ldv,

		_kh_rw_op_new,
		_kh_rw_op_tos,
	};
#define MAPMTD(name,func)	KHPARAM_FUNC_REG_SIMPLE(name, _kh_rw_mt_##func)
	static const struct khParamFuncReg fns[] =
	{
		MAPMTD("Left", left),
		MAPMTD("Right", right),
		MAPMTD("Mid", mid),
		MAPMTD("Length", length),
		MAPMTD("IsHave", is_have),
		MAPMTD("IsEmpty", is_empty),
		MAPMTD("Compare", compare),
		MAPMTD("ToLower", to_lower),
		MAPMTD("ToUpper", to_upper),
		MAPMTD("ToInt", to_int),
		MAPMTD("ToLong", to_long),
		MAPMTD("ToFloat", to_float),
		MAPMTD("ToDouble", to_double),
		MAPMTD("Replace", replace),
		MAPMTD("Split", split),
	};
#undef MAPMTD

	k_type_set_optable(type, &optbl);
	k_type_bind_funcs(type, 1, K_COUNTOF(fns), fns);
}

//
static kpointer _k_redwcs_disp(kBase* ptr)
{
	kRedWcs* self = (kRedWcs*)ptr;
	kcham ret;

	if (self->stt)
	{
		// 정적이면 다시 참조 소생 후 안지움
		k_base_force_revive((kBase*)self);
		return NULL;
	}

	k_h_lock();

	k_slice_ptr_push(khRwSlice, &kh_red_impl.rw_cache, self, &ret);

	if (ret)
	{
		// 캐시에 넣을 수 있으면 정리 안하고 그냥 넣기
#if _SB_DEBUG_
		if (self->len > 8)
			memcpy(self->data, kh_red_impl.rw_mesg, 8 * sizeof(kwchar));
		else
		{
			kint i;

			for (i = 0; i < self->len; i++)
				self->data[i] = L'@';
		}

		self->len = 0;
#endif

		ret = FALSE;
	}
	else
	{
		// 진짜 지움
		k_delete(self->data);

		ret = TRUE;
	}

	k_h_unlock();

	return ret ? self : NULL;
}

// 설정
kcham k_redwcs_set_str(kRedWcs* self, const kwchar* str, kint len)
{
	k_return_value_if_fail(self->data != str, FALSE);

	if (!str)
	{
		k_delete(self->data);
		self->len = 0;
		self->data = NULL;
	}
	else
	{
		if (len <= 0)
			len = (kint)k_wcslen(str);

		self->data = k_renew(self->data, len + 1, kwchar);
		k_wcsncpy(self->data, str, len);
		self->len = len;
	}

	return TRUE;
}

// 데이터
const kwchar* k_redwcs_get_data(kRedWcs* self)
{
	return self->data;
}

// 길이
kint k_redwcs_get_len(kRedWcs* self)
{
	return self->len;
}

// 찾기
kRedWcs* k_redwcs_get_static(const kwchar* str)
{
	kRedWcs* self;

	k_return_value_if_fail(str, NULL);

	k_h_lock();
	k_mukum_ptr_get(khRwMukum, &kh_red_impl.rw_stts, str, &self);
	k_h_unlock();

	return self;
}

// 캐시 크기
void k_redwcs_set_cache_size(kint count)
{
	k_return_if_fail(count >= 0);
	k_return_if_fail(count > 16);

	k_h_lock();
	k_slice_expand(khRwSlice, &kh_red_impl.rw_cache, (ksize_t)count);
	k_h_unlock();
}

// 캐시 크기
kint k_redwcs_get_cache_size(void)
{
	return (kint)k_slice_maximum(&kh_red_impl.rw_cache);
}

// 현재 캐시
kint k_redwcs_get_cache_count(void)
{
	return (kint)k_slice_count(&kh_red_impl.rw_cache);
}

// 만들기
kRedWcs* k_redwcs_new(const kwchar* init, kint len)
{
	kRedWcs* self;

	k_h_lock();
	k_slice_ptr_pop_value(khRwSlice, &kh_red_impl.rw_cache, &self);
	k_h_unlock();

	if (!self)
		self = (kRedWcs*)k_crbs_new(KTYPE_WCS);
	else
		k_base_force_revive((kBase*)self);

	k_redwcs_set_str(self, init, len);

	return self;
}

// 만들기, 정적
kRedWcs* k_redwcs_new_static(const kwchar* init, kint len)
{
	kRedWcs* self = k_redwcs_get_static(init);

	if (!self)
	{
		k_h_lock();
		k_slice_ptr_pop_value(khRwSlice, &kh_red_impl.rw_cache, &self);
		k_h_unlock();

		if (!self)
			self = (kRedWcs*)k_crbs_new(KTYPE_WCS);
		else
			k_base_force_revive((kBase*)self);

		self->stt = TRUE;
		k_redwcs_set_str(self, init, len);

		k_h_lock();
		k_mukum_ptr_add(khRwMukum, &kh_red_impl.rw_stts, self->data, self);
		k_h_unlock();
	}

	return (kRedWcs*)k_base_load((kBase*)self);
}

// 만들기, 복사
kRedWcs* k_redwcs_new_copy(kRedWcs* self)
{
	kRedWcs* dest;

	k_h_lock();
	k_slice_ptr_pop_value(khRwSlice, &kh_red_impl.rw_cache, &dest);
	k_h_unlock();

	if (!dest)
		dest = (kRedWcs*)k_crbs_new(KTYPE_WCS);
	else
		k_base_force_revive((kBase*)dest);

	dest->data = k_renew(dest->data, self->len + 1, kwchar);
	k_wcscpy(dest->data, self->data);
	dest->len = self->len;

	return dest;
}

//
static const kwchar* _kh_rw_op_value(kVar* v, kVar* save, kBwcs64* bs)
{
	char sz[64];

	k_var_agn(save, v);

	switch (v->type)
	{
		case KTYPE_NULL:
		case KTYPE_BOOL:
		case KTYPE_INT:
			k_bwcs_format(bs, 64, L"%d", v->rv.i);
			break;

		case KTYPE_LONG:
			k_bwcs_format(bs, 64, L"%lld", v->rv.i64);
			break;

		case KTYPE_FLOAT:
			k_bwcs_format(bs, 64, L"%f", v->rv.f);
			break;

		case KTYPE_DOUBLE:
			k_bwcs_format(bs, 64, L"%f", v->rv.dbl);
			break;

		case KTYPE_STR:
			bs->len = (kint)k_conv_utf8_to_uni(bs->data, 63, ((kRedStr*)v->rv.p)->data, 0);
			break;

		case KTYPE_WCS:
			bs->len = ((kRedWcs*)v->rv.p)->len;
			return ((kRedWcs*)v->rv.p)->data;

		default:
			k_var_tos(v, sz, 63, NULL);
			bs->len = (kint)k_conv_utf8_to_uni(bs->data, 63, sz, 0);
			break;
	}

	return bs->data;
}

//
static int _kh_rw_op_sig(kVar* ops)
{
	k_var_unload(&ops[0]);
	k_var_set_bool(&ops[0], FALSE);

	return 0;
}

//
static int _kh_rw_op_add(kVar* ops)
{
	kRedWcs* self;
	kVar save[2];
	kBwcs64 bs[2];
	const kwchar* psz[2];
	kwchar* sz;
	ksize_t len;

	psz[0] = _kh_rw_op_value(&ops[0], &save[0], &bs[0]);
	psz[1] = _kh_rw_op_value(&ops[1], &save[1], &bs[1]);

	len = bs[0].len + bs[1].len;
	sz = k_new(len + 1, kwchar);

	memcpy(sz, psz[0], bs[0].len * sizeof(kwchar));
	memcpy(sz + bs[0].len, psz[1], bs[1].len * sizeof(kwchar));
	sz[len] = L'\0';

	self = k_redwcs_new(sz, (kint)len);
	k_var_agn_type(&ops[0], KTYPE_WCS, (kBase*)self);

	k_delete(sz);
	k_var_unload(&save[0]);
	k_var_unload(&save[1]);

	return 0;
}

//
static int _kh_rw_op_blt(kVar* ops)
{
	kVar save[2];
	const kwchar* psz[2];
	kBwcs64 bs[2];

	psz[0] = _kh_rw_op_value(&ops[0], &save[0], &bs[0]);
	psz[1] = _kh_rw_op_value(&ops[1], &save[1], &bs[1]);

	k_var_set_bool(&ops[0], k_wcscmp(psz[0], psz[1]) < 0);

	k_var_unload(&save[0]);
	k_var_unload(&save[1]);

	return 0;
}

//
static int _kh_rw_op_ble(kVar* ops)
{
	kVar save[2];
	const kwchar* psz[2];
	kBwcs64 bs[2];

	psz[0] = _kh_rw_op_value(&ops[0], &save[0], &bs[0]);
	psz[1] = _kh_rw_op_value(&ops[1], &save[1], &bs[1]);

	k_var_set_bool(&ops[0], k_wcscmp(psz[0], psz[1]) > 0);

	k_var_unload(&save[0]);
	k_var_unload(&save[1]);

	return 0;
}

//
static int _kh_rw_op_beq(kVar* ops)
{
	kVar save[2];
	const kwchar* psz[2];
	kBwcs64 bs[2];

	psz[0] = _kh_rw_op_value(&ops[0], &save[0], &bs[0]);
	psz[1] = _kh_rw_op_value(&ops[1], &save[1], &bs[1]);

	k_var_set_bool(&ops[0], k_wcscmp(psz[0], psz[1]) == 0);

	k_var_unload(&save[0]);
	k_var_unload(&save[1]);

	return 0;
}

//
static int _kh_rw_op_bne(kVar* ops)
{
	kVar save[2];
	const kwchar* psz[2];
	kBwcs64 bs[2];

	psz[0] = _kh_rw_op_value(&ops[0], &save[0], &bs[0]);
	psz[1] = _kh_rw_op_value(&ops[1], &save[1], &bs[1]);

	k_var_set_bool(&ops[0], k_wcscmp(psz[0], psz[1]) != 0);

	k_var_unload(&save[0]);
	k_var_unload(&save[1]);

	return 0;
}

//
static int _kh_rw_op_stv(kVar* ops)
{
	kRedWcs* self = (kRedWcs*)k_var_unsafe_base(&ops[0]);
	int ret, ch, idx;

	if (!k_var_safe_int(&ops[1], &idx))
	{
		// 기본
		ret = kh_op_supp_stv(KTYPE_WCS, &ops[0], &ops[1], &ops[2]);
	}
	else if (k_var_safe_int(&ops[2], &ch))
	{
		// 넣을값이 정수고, 범위 안에 있으면
		if ((kuint)idx < (kuint)self->len)
		{
			// 변경
			self->data[idx] = (kwchar)ch;
		}

		ret = 0;
	}
	else
	{
		k_var_unload(&ops[2]);
		// ops[1]은 이미 int임을 확인했으므로 패스
		ret = 0;
	}

	k_base_unload((kBase*)self);

	return ret;
}

//
static int _kh_rw_op_ldv(kVar* ops)
{
	kRedWcs* self = (kRedWcs*)k_var_unsafe_base(&ops[0]);
	int ret, idx;

	if (!k_var_safe_int(&ops[1], &idx))
	{
		// 기본
		ret = kh_op_supp_ldv(KTYPE_WCS, &ops[0], &ops[1], &ops[0]);
	}
	else
	{
		if ((kuint)idx >= (kuint)self->len)
			k_var_zero(&ops[0]);
		else
			k_var_set_int(&ops[0], self->data[idx]);

		ret = 0;
	}

	k_base_unload((kBase*)self);

	return ret;
}

//
static int _kh_rw_op_new(kVar* ret, kint arity, kVar* args)
{
	if (arity != 1)
		k_var_zero(ret);
	else
	{
		kBwcs64 bw;
		kVar save;
		const kwchar* psz = _kh_rw_op_value(&args[0], &save, &bw);
		k_var_agn_type(ret, KTYPE_WCS, (kBase*)k_redwcs_new(psz, bw.len));
	}

	return 0;
}

//
static const char* _kh_rw_op_tos(const kVar* v, char* buf, kint size, kint* out)
{
	kRedWcs* self = (kRedWcs*)k_var_unsafe_base(v);
	size = (kint)k_conv_uni_to_utf8(buf, size, self->data, self->len);

	if (out) *out = size;

	return buf;
}

// wstring left(int cnt)
static kint _kh_rw_mt_left(kScv* s)
{
	kRedWcs* self;
	kint cnt;
	kwchar* psz;

	if (!k_scv_self_type(s, KTYPE_WCS, (kBase**)&self))
		return -1;

	if (!k_scv_arg_int(s, 0, &cnt))
		k_scv_push_self(s, NULL);
	else
	{
		cnt = K_CLAMP(cnt, 0, self->len);
		psz = k_new(cnt + 1, kwchar);
		k_wcsncpy(psz, self->data, cnt);
		k_scv_push_wcs(s, psz, cnt);
		k_delete(psz);
	}

	return 0;
}

// wstring right(int cnt)
static kint _kh_rw_mt_right(kScv* s)
{
	kRedWcs* self;
	kint cnt;
	kwchar* psz;

	if (!k_scv_self_type(s, KTYPE_WCS, (kBase**)&self))
		return -1;

	if (!k_scv_arg_int(s, 0, &cnt))
		k_scv_push_self(s, NULL);
	else
	{
		cnt = K_CLAMP(cnt, 0, self->len);
		psz = k_new(cnt + 1, kwchar);
		k_wcsncpy(psz, self->data + self->len - cnt, cnt);
		k_scv_push_wcs(s, psz, cnt);
		k_delete(psz);
	}

	return 0;
}

// wstring mid(int index, [int count])
static kint _kh_rw_mt_mid(kScv* s)
{
	kRedWcs* self;
	kint idx, cnt;
	kwchar* psz;

	if (!k_scv_self_type(s, KTYPE_WCS, (kBase**)&self))
		return -1;

	if (!k_scv_arg_int(s, 0, &idx))
		k_scv_push_self(s, NULL);
	else
	{
		if (idx >= self->len)
			k_scv_push_null(s);
		else
		{
			if (!k_scv_arg_int(s, 1, &cnt))
				cnt = 0;

			if ((idx + cnt) >= self->len)
				cnt = self->len - idx;

			psz = k_new(cnt + 1, kwchar);
			k_wcsncpy(psz, self->data + idx, cnt);
			k_scv_push_wcs(s, psz, cnt);
			k_delete(psz);
		}
	}

	return 0;
}

// int length()
static kint _kh_rw_mt_length(kScv* s)
{
	kRedWcs* self;

	if (!k_scv_self_type(s, KTYPE_WCS, (kBase**)&self))
		return -1;

	if (k_scv_arity(s) > 0)
		k_scv_push_null(s);
	else
		k_scv_push_int(s, self->len);

	return 0;
}

// bool ishave()
static kint _kh_rw_mt_is_have(kScv* s)
{
	kRedWcs* self;

	if (!k_scv_self_type(s, KTYPE_WCS, (kBase**)&self))
		return -1;

	if (k_scv_arity(s) > 0)
		k_scv_push_null(s);
	else
		k_scv_push_bool(s, self->len != 0);

	return 0;
}

// bool isempty()
static kint _kh_rw_mt_is_empty(kScv* s)
{
	kRedWcs* self;

	if (!k_scv_self_type(s, KTYPE_WCS, (kBase**)&self))
		return -1;

	if (k_scv_arity(s) > 0)
		k_scv_push_null(s);
	else
		k_scv_push_bool(s, self->len == 0);

	return 0;
}

// int compare(var dest[, bool igcase])
static kint _kh_rw_mt_compare(kScv* s)
{
	kRedWcs* self;
	const kwchar* psz;
	kbool igcase;

	if (!k_scv_self_type(s, KTYPE_WCS, (kBase**)&self))
		return -1;

	if (!k_scv_arg_wcs(s, 0, &psz, NULL))
		k_scv_push_null(s);
	else
	{
		if (!k_scv_arg_bool(s, 1, &igcase))
			igcase = FALSE;

		k_scv_push_int(s, igcase ? k_wcsicmp(self->data, psz) : k_wcscmp(self->data, psz));
	}

	return 0;
}

// wstring tolower()
static kint _kh_rw_mt_to_lower(kScv* s)
{
	kRedWcs* self;
	kwchar* psz;

	if (!k_scv_self_type(s, KTYPE_WCS, (kBase**)&self))
		return -1;

	if (k_scv_arity(s) > 0)
		k_scv_push_null(s);
	else
	{
		psz = k_new(self->len, kwchar);
		k_wcscpy(psz, self->data);
		k_wcslwr(psz);
		k_scv_push_wcs(s, psz, self->len);
		k_delete(psz);
	}

	return 0;
}

// wstring toupper()
static kint _kh_rw_mt_to_upper(kScv* s)
{
	kRedWcs* self;
	kwchar* psz;

	if (!k_scv_self_type(s, KTYPE_WCS, (kBase**)&self))
		return -1;

	if (k_scv_arity(s) > 0)
		k_scv_push_null(s);
	else
	{
		psz = k_new(self->len, kwchar);
		k_wcscpy(psz, self->data);
		k_wcsupr(psz);
		k_scv_push_wcs(s, psz, self->len);
		k_delete(psz);
	}

	return 0;
}

// int toint([int base])
static kint _kh_rw_mt_to_int(kScv* s)
{
	kRedWcs* self;
	kint base;

	if (!k_scv_self_type(s, KTYPE_WCS, (kBase**)&self))
		return -1;

	if (!k_scv_arg_int(s, 0, &base))
		base = 10;

	k_scv_push_int(s, base == 10 ? k_wcstoi(self->data) : (kint)k_wcstoi_base(self->data, base));

	return 0;
}

// int tolong([int base])
static kint _kh_rw_mt_to_long(kScv* s)
{
	kRedWcs* self;
	kint base;

	if (!k_scv_self_type(s, KTYPE_WCS, (kBase**)&self))
		return -1;

	if (!k_scv_arg_int(s, 0, &base))
		base = 10;

	k_scv_push_long(s, base == 10 ? k_wcstol(self->data) : (klong)k_wcstol_base(self->data, base));

	return 0;
}

// int tofloat()
static kint _kh_rw_mt_to_float(kScv* s)
{
	kRedWcs* self;

	if (!k_scv_self_type(s, KTYPE_WCS, (kBase**)&self))
		return -1;

	if (k_scv_arity(s) > 0)
		k_scv_push_null(s);
	else
		k_scv_push_float(s, k_wtof(self->data));

	return 0;
}

// int todouble()
static kint _kh_rw_mt_to_double(kScv* s)
{
	kRedWcs* self;

	if (!k_scv_self_type(s, KTYPE_WCS, (kBase**)&self))
		return -1;

	if (k_scv_arity(s) > 0)
		k_scv_push_null(s);
	else
		k_scv_push_double(s, k_wtod(self->data));

	return 0;
}

// wstring replace(wstring from_list, int to)
static kint _kh_rw_mt_replace(kScv* s)
{
	kRedWcs* self;
	const kwchar* rep;
	kwchar* at;
	kint ch;

	if (!k_scv_self_type(s, KTYPE_WCS, (kBase**)&self))
		return -1;

	if (!k_scv_arg_wcs(s, 0, &rep, NULL) ||
		!k_scv_arg_int(s, 1, &ch))
		k_scv_push_null(s);
	else if (k_wcschr(rep, ch))
	{
		// 바꿀 대상이 안에 있으면 오류지...
		k_scv_push_null(s);
	}
	else
	{
		kRedWcs* dest = k_redwcs_new_copy(self);

		for (at = dest->data;;)
		{
			at = k_wcsbrk(at, rep);

			if (!at)
				break;
			else
				*at = (kwchar)ch;
		}

		k_scv_agn_type(s, KTYPE_WCS, (kBase*)dest);
	}

	return 0;
}

// table split(wchar ch[, bool remove_empty])
static kint _kh_rw_mt_split(kScv* s)
{
	kRedWcs* self;
	kVar* sv;

	const kwchar* spl;
	kwchar tmpspl[2];
	kbool rememp = FALSE;

	kRedTbl* tbl = k_redtbl_new();
	kVar kk, vv;
	kint n, len;
	const kwchar* tok;
	kwchar* ctx;

	if (!k_scv_self_type(s, KTYPE_WCS, (kBase**)&self))
		return -1;

	sv = k_scv_arg(s, 0);

	if (k_var_is_int(sv))
	{
		tmpspl[0] = (kwchar)k_var_unsafe_int(sv);
		tmpspl[1] = L'\0';
		spl = tmpspl;
	}
	else
	{
		spl = k_var_unsafe_wcs(sv, NULL);

		if (!spl)
		{
			k_scv_push_null(s);
			return 0;
		}
	}

	if (k_scv_arity(s) > 1)
	{
		sv = k_scv_arg(s, 1);
		k_var_safe_bool(sv, &rememp);
	}

	for (n = 0, tok = k_wcstok(self->data, spl, &ctx); tok; tok = k_wcstok(NULL, spl, &ctx))
	{
		len = (kint)k_wcslen(tok);
		if (rememp && len == 0)
			continue;

		k_var_set_int(&kk, n++);
		k_var_set_wcs(&vv, tok, len);

		k_redtbl_set(tbl, &kk, &vv);
	}

	k_scv_push_type(s, KTYPE_TBL, (kBase*)tbl);

	k_base_unload((kBase*)tbl);

	return 0;
}


//////////////////////////////////////////////////////////////////////////
// 테이블

// 노드
typedef struct khTblNode
{
	struct khTblNode*	sib;
	ksize_t				hash;
	kVar				key;
	kVar				value;
} khTblNode;

//
static kint _kh_rt_op_new(kVar* ret, kint arity, kVar* args);
static const char* _kh_rt_op_tos(const kVar* v, char* buf, kint size, kint* out);
static int _kh_rt_op_stv(kVar* ops);
static int _kh_rt_op_ldv(kVar* ops);

static kint _kh_rt_mt_count(kScv* s);
static kint _kh_rt_mt_remove(kScv* s);
static kint _kh_rt_mt_clear(kScv* s);

//
kType k_redtbl_type(void)
{
	return KTYPE_TBL;
}

//
static void _k_redtbl_type_init(kType type)
{
	static struct khOpTable optbl =
	{
		NULL,

		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,

		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,

		_kh_rt_op_stv,
		_kh_rt_op_ldv,

		_kh_rt_op_new,
		_kh_rt_op_tos,
	};
#define MAPMTD(name,func)	KHPARAM_FUNC_REG_SIMPLE(name, _kh_rt_mt_##func)
	static const struct khParamFuncReg fns[] =
	{
		MAPMTD("Count", count),
		MAPMTD("Remove", remove),
		MAPMTD("Clear", clear),
	};
#undef MAPMTD

	k_type_set_optable(type, &optbl);
	k_type_bind_funcs(type, 1, (kint)K_COUNTOF(fns), fns);
}

//
static kBase* _k_redtbl_cnst(kpointer ptr)
{
	kRedTbl* self = (kRedTbl*)ptr;

	//
	self->lock = k_mutex_new();
	self->bucket = K_MIN_HASH;
	self->nodes = k_new_zero(K_MIN_HASH, khTblNode*);

	//
	self->anys = k_new_1(khTblAnyCb);
	k_arr_init(khTblAnyCb, self->anys, 0);

	//
	return (kBase*)self;
}

//
static kpointer _k_redtbl_disp(kBase* ptr)
{
	kRedTbl* self = (kRedTbl*)ptr;
	kcham ret;
	ksize_t i;

	k_h_lock();
	k_slice_ptr_push(khRtSlice, &kh_red_impl.rt_cache, self, &ret);
	k_h_unlock();

	if (ret)
	{
		// 캐시에 넣음, 정리하고 지우고, 소생은 나중에
		k_mutex_enter(self->lock);

		for (i = 0; i < self->count; i++)
		{
			khTblNode* node = self->anode[i];
			k_var_unload(&node->value);
			k_delete(node);
		}

		memset(self->nodes, 0, self->bucket * sizeof(khTblNode*));
		self->count = 0;

		if (k_arr_is_have(self->anys))
		{
			for (i = 0; i < (ksize_t)k_arr_count(self->anys); i++)
			{
				kanycb* cb = &k_arr_nth(self->anys, i);
				((kfunc_1)cb->func)(cb->data);
			}

			k_arr_clear(khTblAnyCb, self->anys);
		}

		if (self->parent)
		{
			k_base_unload((kBase*)self->parent);
			self->parent = NULL;
		}

		k_mutex_leave(self->lock);

		ret = FALSE;
	}
	else
	{
		// 진짜 지움
		k_mutex_enter(self->lock);

		for (i = 0; i < self->count; i++)
		{
			khTblNode* node = self->anode[i];
			k_var_unload(&node->value);
			k_delete(node);
		}

		k_delete(self->anode);
		k_delete(self->nodes);

		if (k_arr_is_have(self->anys))
		{
			for (i = 0; i < (ksize_t)k_arr_count(self->anys); i++)
			{
				kanycb* cb = &k_arr_nth(self->anys, i);
				((kfunc_1)cb->func)(cb->data);
			}
		}

		k_arr_disp(khTblAnyCb, self->anys);
		k_delete(self->anys);

		if (self->parent)
			k_base_unload((kBase*)self->parent);

		k_mutex_leave(self->lock);
		k_mutex_delete(self->lock);

		ret = TRUE;
	}

	return ret ? self : NULL;
}

// 크기
static void _k_redtbl_hash_resize(kRedTbl* self)
{
	khTblNode** newnodes;
	ksize_t newbucket, i;

	newbucket = k_primeclose(self->count);
	newbucket = K_CLAMP(newbucket, K_MIN_HASH, K_MAX_HASH);
	newnodes = k_new_zero(newbucket, khTblNode*);

	for (i = 0; i < self->bucket; i++)
	{
		khTblNode* node;
		khTblNode* sib;
		ksize_t mask;

		for (node = self->nodes[i]; node; node = sib)
		{
			sib = node->sib;
			mask = node->hash % newbucket;
			node->sib = newnodes[mask];
			newnodes[mask] = node;
		}
	}

	k_delete(self->nodes);
	self->nodes = newnodes;
	self->bucket = newbucket;
}

// 검사
static void _k_redtbl_hash_test(kRedTbl* self)
{
	ksize_t cnt = self->count;
	ksize_t bkt = self->bucket;

	if ((bkt >= 3 * cnt && bkt > K_MIN_HASH) ||
		(3 * bkt <= cnt && bkt < K_MAX_HASH))
		_k_redtbl_hash_resize(self);
}

// 찾기
static khTblNode** _k_redtbl_hash_look_up(kRedTbl* self, const kVar* key, ksize_t hash)
{
	khTblNode* node;
	khTblNode** pnode = &self->nodes[hash % self->bucket];

	while ((node = *pnode) != NULL)
	{
		if (node->hash == hash && k_redtbl_key_eq(key, &node->key))
			break;

		pnode = &node->sib;
	}

	return pnode;
}

// 노드로 찾기
static khTblNode* _k_redtbl_hash_look_node(kRedTbl* self, const kVar* key)
{
	return *_k_redtbl_hash_look_up(self, key, k_redtbl_key_hash(key));
}

// 노드찾기+해시
static khTblNode* _k_redtbl_hash_look_node_hash(kRedTbl* self, const kVar* key, ksize_t hash)
{
	return *_k_redtbl_hash_look_up(self, key, hash);
}

// 넣기
static void _k_redtbl_hash_append(kRedTbl* self, khTblNode** pnode, ksize_t hash, const kVar* key, const kVar* value)
{
	khTblNode* node;
	ksize_t cnt;

	node = k_new_1(khTblNode);
	node->sib = *pnode;
	node->hash = hash;
	k_var_agn(&node->key, key);
	k_var_agn(&node->value, value);

	*pnode = node;

	cnt = self->count + 1;

	if (self->capa <= cnt)
	{
		self->capa = self->capa + self->capa / 2;

		if (self->capa < cnt)
			self->capa = cnt;

		self->anode = k_renew(self->anode, self->capa, khTblNode*);
	}

	self->anode[self->count] = node;

	self->count++;

	_k_redtbl_hash_test(self);
}

// 추가
static void _k_redtbl_hash_add(kRedTbl* self, const kVar* key, const kVar* value)
{
	ksize_t hash = k_redtbl_key_hash(key);
	khTblNode** pnode = _k_redtbl_hash_look_up(self, key, hash);

	if (*pnode)
	{
		// 대체
		khTblNode* node = *pnode;
		k_var_unload(&node->value);
		k_var_agn(&node->value, value);
	}
	else
	{
		// 넣기
		_k_redtbl_hash_append(self, pnode, hash, key, value);
	}
}

// 노드 제거
static void _k_redtbl_hash_erase_node(kRedTbl* self, khTblNode*** ppnode)
{
	khTblNode** pnode = *ppnode;
	khTblNode* node = *pnode;
	ksize_t i;

	*pnode = node->sib;

	for (i = 0; i < self->count; i++)
	{
		if (self->anode[i] == node)
		{
			// 빠른제거
			self->anode[i] = self->anode[self->count - 1];
			break;
		}
	}

	self->count--;

	k_var_unload(&node->value);
	k_delete(node);
}

// 제거
static kcham _k_redtbl_hash_erase(kRedTbl* self, const kVar* key)
{
	khTblNode** pnode = _k_redtbl_hash_look_up(self, key, k_redtbl_key_hash(key));

	if (!(*pnode))
		return FALSE;
	else
	{
		_k_redtbl_hash_erase_node(self, &pnode);
		return TRUE;
	}
}

// 모두 제거
static void _k_redtbl_hash_erase_all(kRedTbl* self)
{
	if (self->count)
	{
		ksize_t i;

		for (i = 0; i < self->count; i++)
		{
			khTblNode* node = self->anode[i];
			k_var_unload(&node->value);
			k_delete(node);
		}

		self->count = 0;
		memset(self->nodes, 0, self->bucket * sizeof(khTblNode*));
	}
}

// 테이블 추가
static void _k_redtbl_hash_add_tbl(kRedTbl* self, kRedTbl* from)
{
	khTblNode** pnode;
	khTblNode* node;
	khTblNode* src;
	ksize_t i, n;

	i = self->count + from->count + 1;

	if (self->capa < i)
	{
		self->capa = self->capa + self->capa / 2;

		if (self->capa < i)
			self->capa = i;

		self->anode = k_renew(self->anode, self->capa, khTblNode*);
	}

	for (n = self->count, i = 0; i < from->count; i++)
	{
		src = from->anode[i];

		pnode = _k_redtbl_hash_look_up(self, &src->key, src->hash);
		node = *pnode;

		if (node)
		{
			k_var_unload(&node->value);
			k_var_set(&node->value, &src->value);
		}
		else
		{
			node = k_new_1(khTblNode);
			node->sib = NULL;
			node->hash = src->hash;
			k_var_agn(&node->key, &src->key);
			k_var_set(&node->value, &src->value);

			*pnode = node;
			self->anode[n++] = node;
		}
	}

	self->count = n;

	_k_redtbl_hash_test(self);
}

// 스토리지 추가
static void _k_redtbl_anys_add(kRedTbl* self, kpointer data, kfunc_1 func)
{
	kanycb cb;

	cb.func = (kfunc)func;
	cb.data = data;

	k_arr_add(khTblAnyCb, self->anys, cb);
}

// 스토리지 콜백
static kcham _k_redtbl_anys_find_callback(const kanycb* left, kpointer right)
{
	return left->data == right;
}

// 잠금
void k_redtbl_enter_lock(kRedTbl* self)
{
	k_mutex_enter(self->lock);
}

// 풀기
void k_redtbl_leave_lock(kRedTbl* self)
{
	k_mutex_leave(self->lock);
}

// 얻어보기
kcham k_redtbl_try(kRedTbl* self, const kVar* key, kVar* value)
{
	khTblNode* node;
	kcham ret;

	k_return_value_if_fail(key != NULL, FALSE);

	k_mutex_enter(self->lock);

	node = _k_redtbl_hash_look_node(self, key);

	if (!node)
		ret = FALSE;
	else
	{
		if (value)
			k_var_agn(value, &node->value);

		ret = TRUE;
	}

	k_mutex_leave(self->lock);

	return ret;
}

// 얻기
kcham k_redtbl_get(kRedTbl* self, const kVar* key, kVar* value)
{
	khTblNode* node;
	kcham ret;

	k_return_value_if_fail(key != NULL, FALSE);

	k_mutex_enter(self->lock);

	node = _k_redtbl_hash_look_node(self, key);

	if (!value)
		ret = node != NULL;
	else
	{
		if (!node)
		{
			k_var_zero(value);
			ret = FALSE;
		}
		else
		{
			k_var_agn(value, &node->value);
			ret = TRUE;
		}
	}

	k_mutex_leave(self->lock);

	return ret;
}

// 넣기
void k_redtbl_set(kRedTbl* self, const kVar* key, const kVar* value)
{
	k_return_if_fail(key != NULL);
	k_return_if_fail(value != NULL);

	if (!k_var_test_key(key))
	{
		k_var_unload((kVar*)key);
		k_var_unload((kVar*)value);
	}
	else
	{
		k_mutex_enter(self->lock);
		_k_redtbl_hash_add(self, key, value);
		k_mutex_leave(self->lock);
	}
}

// 얻기
kcham k_redtbl_recursive_try(kRedTbl* self, const kVar* key, kVar* value)
{
	khTblNode* node;
	ksize_t hash;

	k_return_value_if_fail(key != NULL, FALSE);

	hash = k_redtbl_key_hash(key);

	do
	{
		k_mutex_enter(self->lock);
		node = _k_redtbl_hash_look_node_hash(self, key, hash);

		if (node)
		{
			if (value)
				k_var_agn(value, &node->value);

			k_mutex_leave(self->lock);
			return TRUE;
		}

		k_mutex_leave(self->lock);
		self = self->parent;
	} while (self != NULL);

	return FALSE;
}

// 얻기
kcham k_redtbl_recursive_get(kRedTbl* self, const kVar* key, kVar* value)
{
	khTblNode* node;
	ksize_t hash;

	k_return_value_if_fail(key != NULL, FALSE);

	hash = k_redtbl_key_hash(key);

	do
	{
		k_mutex_enter(self->lock);
		node = _k_redtbl_hash_look_node_hash(self, key, hash);

		if (node)
		{
			if (value)
				k_var_agn(value, &node->value);

			k_mutex_leave(self->lock);
			return TRUE;
		}

		k_mutex_leave(self->lock);
		self = self->parent;
	} while (self != NULL);

	if (value)
		k_var_zero(value);

	return FALSE;
}

// 넣기
kcham k_redtbl_recursive_set(kRedTbl* self, const kVar* key, const kVar* value)
{
	kRedTbl* save;
	khTblNode** pnode;
	khTblNode* node;
	ksize_t hash;

	k_return_value_if_fail(key != NULL, FALSE);
	k_return_value_if_fail(value != NULL, FALSE);

	if (!k_var_test_key(key))
	{
		k_var_unload((kVar*)key);
		k_var_unload((kVar*)value);
		return FALSE;
	}

	save = self;
	hash = k_redtbl_key_hash(key);

	do
	{
		k_mutex_enter(self->lock);
		node = _k_redtbl_hash_look_node_hash(self, key, hash);

		if (node)
		{
			k_var_unload(&node->value);
			k_var_agn(&node->value, value);
			k_mutex_leave(self->lock);
			return TRUE;
		}

		k_mutex_leave(self->lock);
		self = self->parent;
	} while (self != NULL);

	self = save;
	k_mutex_enter(self->lock);
	pnode = _k_redtbl_hash_look_up(self, key, hash);
	_k_redtbl_hash_append(self, pnode, hash, key, value);
	k_mutex_leave(self->lock);

	return TRUE;
}

// 얻기
kcham k_redtbl_unsafe_get(kRedTbl* self, const kVar* key, kVar* value, ksize_t hash)
{
	khTblNode* node = _k_redtbl_hash_look_node_hash(self, key, hash);

	if (!node)
		return FALSE;
	else
	{
		k_var_agn(value, &node->value);
		return TRUE;
	}
}

// 넣기
kcham k_redtbl_unsafe_set(kRedTbl* self, const kVar* key, const kVar* value, ksize_t hash)
{
	khTblNode* node = _k_redtbl_hash_look_node_hash(self, key, hash);

	if (!node)
		return FALSE;
	else
	{
		k_var_unload(&node->value);
		k_var_agn(&node->value, value);
		return TRUE;
	}
}

// 넣기
void k_redtbl_unsafe_append(kRedTbl* self, const kVar* key, const kVar* value, ksize_t hash)
{
	khTblNode** pnode = _k_redtbl_hash_look_up(self, key, hash);
	_k_redtbl_hash_append(self, pnode, hash, key, value);
}

// 제거
kcham k_redtbl_remove(kRedTbl* self, const kVar* key)
{
	kcham ret;

	k_return_value_if_fail(key != NULL, FALSE);

	k_mutex_enter(self->lock);
	ret = _k_redtbl_hash_erase(self, key);
	k_mutex_leave(self->lock);

	return ret;
}

// 모두 제거
void k_redtbl_clear(kRedTbl* self)
{
	k_mutex_enter(self->lock);
	_k_redtbl_hash_erase_all(self);
	k_mutex_leave(self->lock);
}

// 복사
void k_redtbl_add_tbl(kRedTbl* self, kRedTbl* from)
{
	k_return_if_fail(from != NULL);

	k_mutex_enter(self->lock);
	k_mutex_enter(from->lock);

	_k_redtbl_hash_add_tbl(self, from);

	k_mutex_leave(from->lock);
	k_mutex_leave(self->lock);
}

// 인덱스
kcham k_redtbl_get_nth(kRedTbl* self, kint index, kVar* key, kVar* value)
{
	if ((ksize_t)index >= self->count)
		return FALSE;
	else
	{
		khTblNode* node = self->anode[index];

		if (key)
			k_var_agn(key, &node->key);

		if (value)
			k_var_agn(value, &node->value);

		return TRUE;
	}
}

// 버킷
kint k_redtbl_get_bucket(kRedTbl* self)
{
	return (kint)self->bucket;
}

// 개수
kint k_redtbl_get_count(kRedTbl* self)
{
	return (kint)self->count;
}

// 부모
void k_redtbl_set_parent(kRedTbl* self, kRedTbl* parent)
{
	if (self->parent)
		k_base_unload((kBase*)self->parent);

	self->parent = parent;

	if (parent)
		k_base_load((kBase*)parent);
}

// 찾기
kcham k_redtbl_find(kRedTbl* self, const char* find, kVar* value)
{
	char buf[260], *ps, *pe;
	kVar kv, vv;
	kcham isfrst;

	k_return_value_if_fail(find != NULL, FALSE);

	k_strncpy(buf, find, 259);
	pe = ps = buf;
	isfrst = TRUE;

	while (self && pe && *pe)
	{
		switch (*pe)
		{
			case '.':
			case '[':
				*pe = '\0';

				if (!k_var_try_sym(&kv, ps))
				{
					self = NULL;
					break;
				}

				if (isfrst)
				{
					if (!k_redtbl_recursive_try(self, &kv, &vv))
					{
						self = NULL;
						break;
					}

					isfrst = FALSE;
				}
				else
				{
					if (!k_redtbl_try(self, &kv, &vv))
					{
						self = NULL;
						break;
					}
				}

				if (!k_var_safe_type(&vv, KTYPE_TBL, (kBase**)&self))
					self = NULL;

				ps = pe + 1;
				pe = ps;
				break;

			case ']':
				while (pe && *pe && *pe != ']')
					pe++;

				if (*pe == ']')
				{
					*pe = '\0';
					k_var_set_int(&kv, k_atoi(ps));

					if (!k_redtbl_try(self, &kv, &vv))
					{
						self = NULL;
						break;
					}

					ps = pe + 1;

					if (!k_var_safe_type(&vv, KTYPE_TBL, (kBase**)&self))
					{
						if (*ps == '\0')
						{
							if (value)
								*value = vv;

							return TRUE;
						}
					}

					while (ps && *ps && (*ps == '.' || *ps == '['))
						ps++;

					pe = ps;
				}

				break;
		}

		pe++;
	}

	if (!self)
		return FALSE;
	else
	{
		if (!k_var_try_sym(&kv, ps))
			return FALSE;

		if (!k_redtbl_try(self, &kv, &vv))
			return FALSE;
		else
		{
			if (value)
				*value = vv;

			return TRUE;
		}
	}
}

// 스토리지
void k_redtbl_bind_anys(kRedTbl* self, kpointer data, kfunc_1 disposer)
{
	k_return_if_fail(data != NULL && disposer != NULL);

	k_mutex_enter(self->lock);
	_k_redtbl_anys_add(self, data, disposer);
	k_mutex_leave(self->lock);
}

// 제거
kcham k_redtbl_unbind_anys(kRedTbl* self, kpointer data, kcham needcall)
{
	kint idx;
	kcham ret;

	k_return_value_if_fail(data != NULL, FALSE);

	k_mutex_enter(self->lock);

	k_arr_contains_cmp(khTblAnyCb, self->anys, _k_redtbl_anys_find_callback, data, &idx);

	if (idx < 0)
		ret = FALSE;
	else
	{
		if (needcall)
		{
			kanycb* cb = &k_arr_nth(self->anys, idx);
			((kfunc_1)cb->func)(cb->data);
		}

		k_arr_remove_nth(khTblAnyCb, self->anys, idx);

		ret = TRUE;
	}

	k_mutex_leave(self->lock);

	return ret;
}

// 바인드
kcham k_redtbl_bind_param(kRedTbl* self, khParamDesc* param, kcham store)
{
	kVar kv, vv;

	k_return_value_if_fail(param != NULL && param->name != NULL, FALSE);

	k_var_set_sym(&kv, param->key);
	k_var_set_param(&vv, param);

	k_mutex_enter(self->lock);
	_k_redtbl_hash_add(self, &kv, &vv);

	if (store)
		_k_redtbl_anys_add(self, param, (kfunc_1)kh_param_free);

	k_mutex_leave(self->lock);

	return TRUE;
}

// 함수 바인드
kcham k_redtbl_bind_func(kRedTbl* self, kRedFn* rfn)
{
	kVar kv, vv;

	k_return_value_if_fail(rfn != NULL, FALSE);

	k_var_set_sym_flex(&kv, k_redfn_get_name(rfn));
	k_var_set_type(&vv, KTYPE_FN, (kBase*)rfn);

	k_mutex_enter(self->lock);
	_k_redtbl_hash_add(self, &kv, &vv);
	k_mutex_leave(self->lock);

	return FALSE;
}

// 언세이프 바인드
void k_redtbl_unsafe_bind_param(kRedTbl* self, khParamDesc* param, kcham store)
{
	kVar kv, vv;

	k_var_set_sym(&kv, param->key);
	k_var_set_param(&vv, param);

	_k_redtbl_hash_add(self, &kv, &vv);

	if (store)
		_k_redtbl_anys_add(self, param, (kfunc_1)kh_param_free);
}

// 언세이프 함수 바인드
void k_redtbl_unsafe_bind_func(kRedTbl* self, kRedFn* rfn)
{
	kVar kv, vv;

	k_var_set_sym_flex(&kv, k_redfn_get_name(rfn));
	k_var_set_type(&vv, KTYPE_FN, (kBase*)rfn);

	_k_redtbl_hash_add(self, &kv, &vv);
}

// 해시
ksize_t k_redtbl_key_hash(const kVar* key)
{
	return k_ptrhash(K_CAST_UINT_TO_PTR(key->rv.i));
}

// 비교
kcham k_redtbl_key_eq(const kVar* k1, const kVar* k2)
{
	return k1->type == k2->type && k1->rv.i == k2->rv.i;
}

// 캐시 크기
void k_redtbl_set_cache_size(kint count)
{
	k_return_if_fail(count >= 0);
	k_return_if_fail(count > 16);

	k_h_lock();
	k_slice_expand(khRtSlice, &kh_red_impl.rt_cache, (ksize_t)count);
	k_h_unlock();
}

// 캐시 크기
kint k_redtbl_get_cache_size(void)
{
	return (kint)k_slice_maximum(&kh_red_impl.rt_cache);
}

// 현재 캐시
kint k_redtbl_get_cache_count(void)
{
	return (kint)k_slice_count(&kh_red_impl.rt_cache);
}

// 출력
void k_redtbl_debug_print(kRedTbl* self)
{
	ksize_t i;
	char sk[260];
	char sv[260];
	const char* psz;

	for (i = 0; i < self->count; i++)
	{
		khTblNode* n = self->anode[i];

		if (n->key.type == KTYPE_SYMBOL)
			k_strcpy(sk, k_symunstr(n->key.rv.u));
		else
			k_snprintf(sk, 259, "%d", n->key.rv.i);

		psz = k_var_tos(&n->value, sv, 259, NULL);

		k_mesgf("%3d [%08X] (%d)%s -> (%s:%d)%s\n", i,
			(kuint)n->hash, n->key.rv.i, sk, k_type_get_name(n->value.type),
			k_type_is_value(n->value.type) ? 0 : k_base_get_ref((kBase*)n->value.rv.p), psz);
	}
}

// 만들기
kRedTbl* k_redtbl_new(void)
{
	kRedTbl* self;

	k_h_lock();
	k_slice_ptr_pop_value(khRtSlice, &kh_red_impl.rt_cache, &self);
	k_h_unlock();

	if (!self)
		self = (kRedTbl*)k_crbs_new(KTYPE_TBL);
	else
		k_base_force_revive((kBase*)self);

	return self;
}

// 만들기, 복사
kRedTbl* k_redtbl_new_copy(kRedTbl* from)
{
	kRedTbl* self = k_redtbl_new();

	if (from)
	{
		k_mutex_enter(from->lock);
		_k_redtbl_hash_add_tbl(self, from);
		k_mutex_leave(from->lock);
	}

	return self;
}

// op new
static kint _kh_rt_op_new(kVar* ret, kint arity, kVar* args)
{
	kRedTbl* self = k_redtbl_new();

	if (arity == 1 && k_var_test_type_of(&args[0], KTYPE_TBL))
	{
		// 인수가 1개고, 그게 테이블으면 복사
		kRedTbl* from = (kRedTbl*)k_var_unsafe_base(&args[0]);
		k_mutex_enter(from->lock);
		_k_redtbl_hash_add_tbl(self, from);
		k_mutex_leave(from->lock);
	}
	else if (arity != 0)
	{
		// 넣기
		khTblNode** pnode;
		kVar key;
		ksize_t hash;

		key.type = KTYPE_INT;

		for (key.rv.u = 0; key.rv.u < (kuint)arity; key.rv.u++)
		{
			hash = k_redtbl_key_hash(&key);
			pnode = _k_redtbl_hash_look_up(self, &key, hash);
			_k_redtbl_hash_append(self, pnode, hash, &key, &args[key.rv.u]);
			// 인수는 자동으로 지워지므로 참조해야함
			k_var_load(&args[key.rv.u]);
		}
	}

	k_var_agn_type(ret, KTYPE_TBL, (kBase*)self);

	return 0;
}

// op str
static const char* _kh_rt_op_tos(const kVar* v, char* buf, kint size, kint* out)
{
	kRedTbl* self = (kRedTbl*)k_var_unsafe_base(v);
	kint len = k_snprintf(buf, size, "{%d}", self->count);

	if (out) *out = len;

	return buf;
}

// op stv
static int _kh_rt_op_stv(kVar* ops)
{
	kRedTbl* self = (kRedTbl*)k_var_unsafe_base(&ops[0]);
	k_redtbl_set(self, &ops[1], &ops[2]);
	// 참조를 해제하지 않으므로 해제
	k_base_unload((kBase*)self);
	return 0;
}

// op ldv
static int _kh_rt_op_ldv(kVar* ops)
{
	kRedTbl* self = (kRedTbl*)k_var_unsafe_base(&ops[0]);

	if (!k_redtbl_try(self, &ops[1], &ops[0]))
	{
		int ret = kh_op_supp_ldv(KTYPE_TBL, &ops[0], &ops[1], &ops[0]);

		if (ret != 0)
			return ret;
	}

	// 참조를 해제하지 않으므로 해제
	k_base_unload((kBase*)self);
	return 0;
}

// int count()
static kint _kh_rt_mt_count(kScv* s)
{
	kRedTbl* self;

	if (!k_scv_self_type(s, KTYPE_TBL, (kBase**)&self))
		return -1;

	if (k_scv_arity(s) > 0)
		k_scv_push_null(s);
	else
		k_scv_push_int(s, (kint)self->count);

	return 0;
}

// int remove(key1[,key2,...])
static kint _kh_rt_mt_remove(kScv* s)
{
	kRedTbl* self;
	kint cnt, i;

	if (!k_scv_self_type(s, KTYPE_TBL, (kBase**)&self))
		return -1;

	cnt = k_scv_arity(s);

	if (cnt < 1)
		k_scv_push_int(s, 0);
	else
	{
		kint acc = 0;

		for (i = 0; i < cnt; i++)
		{
			kVar* pk = k_scv_arg(s, i);

			if (k_var_test_key(pk))
			{
				if (k_redtbl_remove(self, pk))
					acc++;
			}
			else if (k_var_test_type_of(pk, KTYPE_STR))
			{
				kRedStr* str = (kRedStr*)k_var_unsafe_base(pk);
				kuint sym = k_redstr_build_sym(str, TRUE);

				if (sym != 0)
				{
					kVar kk;
					k_var_set_sym(&kk, sym);

					if (k_redtbl_remove(self, &kk))
						acc++;
				}
			}
		}

		k_scv_push_int(s, acc);
	}

	return 0;
}

// bool clear()
static kint _kh_rt_mt_clear(kScv* s)
{
	kRedTbl* self;

	if (!k_scv_self_type(s, KTYPE_TBL, (kBase**)&self))
		return -1;

	if (k_scv_arity(s) > 0)
		k_scv_push_bool(s, FALSE);
	else
	{
		k_redtbl_clear(self);
		k_scv_push_bool(s, TRUE);
	}

	return 0;
}


//////////////////////////////////////////////////////////////////////////
// 함수

//
static const char* _k_redfn_op_tos(const kVar* v, char* buf, kint size, kint* out);

static void _kh_fncd_finalize(khFnCode* fc);
static void _kh_fncd_reset(khFnCode* fc);

//
kType k_redfn_type(void)
{
	return KTYPE_FN;
}

//
static void _k_redfn_type_init(kType type)
{
	static struct khOpTable optbl =
	{
		NULL,

		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,

		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL,

		NULL,
		NULL,

		NULL,
		_k_redfn_op_tos,
	};

	k_type_set_optable(type, &optbl);
}

//
static kpointer _k_redfn_disp(kBase* ptr)
{
	kRedFn* self = (kRedFn*)ptr;
	kcham ret;

	k_h_lock();

	k_slice_ptr_push(htRfSlice, &kh_red_impl.rf_cache, self, &ret);

	if (ret)
	{
		// 캐시
		if (self->func)
			self->func = NULL;
		else if (self->code)
		{
			_kh_fncd_reset(self->code);

			if (self->objs)
			{
				k_ctnr_loopeach(khObjCtn, self->objs, k_base_unload);
				k_ctnr_reset(khObjCtn, self->objs);
			}
		}

		ret = FALSE;
	}
	else
	{
		// 지움
		if (self->code)
		{
			_kh_fncd_finalize(self->code);
			k_delete(self->code);

			if (self->objs)
			{
				k_ctnr_loopeach(khObjCtn, self->objs, k_base_unload);
				k_ctnr_disp(khObjCtn, self->objs);
				k_delete(self->objs);
			}
		}

		ret = TRUE;
	}

	k_h_unlock();

	return ret ? self : NULL;
}

// 코드 데이터
kcham k_redfn_set_code(kRedFn* self, const khFuncData* fd)
{
	khFnCode* code;
	kuint size;
	kssize_t i;

	if (self->code)
	{
		// 파일 이름이 있으면 이미 만들어져 있는 것임
		code = self->code;
		k_return_value_if_fail(code->filename == NULL, FALSE);
	}
	else
	{
		// 새로 만들기
		code = k_new_0(khFnCode);
		self->code = code;
	}

	// 이름
	self->name = k_syminflx(fd->name);

	if (fd->filename)
		code->filename = k_strdup(fd->filename);
	else
		k_asprintf(&code->filename, "kh_fn_%d", kh_red_impl.rf_index++);

	// 빌드 시간
	self->bdt = fd->bdt;

	// 코드
	size = fd->code.loc;

	if (size)
	{
		code->data.size = size;
		code->data.code = k_renew(code->data.code, size, kuint);
		code->data.line = k_renew(code->data.line, size, kushort);
		memcpy(code->data.code, fd->code.code, size * sizeof(kuint));
		memcpy(code->data.line, fd->code.line, size * sizeof(kushort));
	}

	// 스택
	code->si.stks = 0;
	code->si.args = fd->args;
	code->si.lcls = fd->lcls - fd->args;
	code->si.vars = fd->lcls;

	// 상수 테이블
	if (k_parr_is_have(&fd->reds))
	{
		if (self->objs)
			k_ctnr_resize(khObjCtn, self->objs, k_parr_count(&fd->reds));
		else
		{
			self->objs = k_new_0(khObjCtn);
			k_ctnr_init(khObjCtn, self->objs, k_parr_count(&fd->reds));
		}

		for (i = 0; i < k_parr_count(&fd->reds); i++)
			k_ctnr_set(self->objs, i, (kBase*)k_parr_nth(&fd->reds, i));
	}

	// 지역 변수

	// 심볼
	if (k_arr_is_have(&fd->syms))
	{
		if (code->syms)
			k_ctnr_resize(kUintCtn, code->syms, k_arr_count(&fd->syms));
		else
		{
			code->syms = k_new_0(kUintCtn);
			k_ctnr_init(kUintCtn, code->syms, k_arr_count(&fd->syms));
		}

		for (i = 0; i < k_parr_count(&fd->syms); i++)
			k_ctnr_set(code->syms, i, k_arr_nth(&fd->syms, i));
	}

	return TRUE;
}

// 이름
const char* k_redfn_get_name(kRedFn* self)
{
	return self->name;
}

// 파일 이름
const char* k_redfn_get_filename(kRedFn* self)
{
	return /*!self->func &&*/ self->code ? self->code->filename : "noname";
}

// 이름은 count인데 오브젝트 카운트임
kint k_redfn_get_obj_count(kRedFn* self)
{
	return self->objs ? (kint)k_ctnr_count(self->objs) : 0;
}

// 오브젝트
kBase* k_redfn_get_obj_nth(kRedFn* self, kint nth)
{
	k_return_value_if_fail(self->objs, NULL);
	k_return_value_if_fail((ksize_t)nth < k_ctnr_count(self->objs), NULL);

	return k_ctnr_nth(self->objs, nth);
}

//
khParamCallFunc k_redfn_get_native_call(kRedFn* self)
{
	return self->func;
}

//
kRedFn* k_redfn_get_parent(kRedFn* self)
{
	return self->parent;
}

//
khFnCode* k_redfn_get_code(kRedFn* self)
{
	return self->code;
}

//
kcham k_redfn_get_info(kRedFn* self, struct kRedFnInfo* info)
{
	khFnCode* code = self->code;

	k_return_value_if_fail(info, FALSE);
	k_return_value_if_fail(code, FALSE);

	info->size = code->data.size;
	info->stack = code->si.stks;
	info->arity = code->si.args;
	info->local = code->si.lcls;
	info->var = code->si.vars;
	info->sym = code->syms ? (kint)k_ctnr_count(code->syms) : 0;

	return TRUE;
}

//
kuint* k_redfn_get_data(kRedFn* self)
{
	return /*!self->func &&*/ self->code ? self->code->data.code : NULL;
}

//
kushort* k_redfn_get_line(kRedFn* self)
{
	return /*!self->func &&*/ self->code ? self->code->data.line : NULL;
}

//
kuint k_redfn_get_sym_nth(kRedFn* self, kint nth)
{
	khFnCode* code = self->code;
	return code && code->syms && (ksize_t)nth < k_ctnr_count(code->syms) ? k_ctnr_nth(code->syms, nth) : 0;
}

//
kcham k_redfn_debug_file(kRedFn* self, kFile* file)
{
	k_return_value_if_fail(file, FALSE);

	return kh_dasm_write_file(file, self);
}

//
kcham k_redfn_debug_output(kRedFn* self, const char* filename)
{
	k_return_value_if_fail(filename, FALSE);

	return kh_dasm_write_output(filename, self);
}

// 캐시 크기
void k_redfn_set_cache_size(kint count)
{
	k_return_if_fail(count > 16);

	k_h_lock();
	k_slice_expand(htRfSlice, &kh_red_impl.rf_cache, (ksize_t)count);
	k_h_unlock();
}

// 캐시 크기
kint k_redfn_get_cache_size(void)
{
	return (kint)k_slice_maximum(&kh_red_impl.rf_cache);
}

// 현재 캐시
kint k_redfn_get_cache_count(void)
{
	return (kint)k_slice_count(&kh_red_impl.rf_cache);
}

// 만들기
kRedFn* k_redfn_new(void)
{
	kRedFn* self;

	k_h_lock();
	k_slice_ptr_pop_value(htRfSlice, &kh_red_impl.rf_cache, &self);
	k_h_unlock();

	if (!self)
		self = (kRedFn*)k_crbs_new(KTYPE_FN);
	else
		k_base_force_revive((kBase*)self);

	return self;
}

// op tos
static const char* _k_redfn_op_tos(const kVar* v, char* buf, kint size, kint* out)
{
	kRedFn* self = (kRedFn*)k_var_unsafe_base(v);
	int len = k_snprintf(buf, size, "{%s}", self->name ? self->name : "noname");

	if (out) *out = len;

	return buf;
}

//
static void _kh_fncd_finalize(khFnCode* self)
{
	if (self->syms)
	{
		k_ctnr_disp(kUintCtn, self->syms);
		k_delete(self->syms);
	}

	k_delete(self->data.code);
	k_delete(self->data.line);

	k_delete(self->filename);
}

//
static void _kh_fncd_reset(khFnCode* self)
{
	if (self->syms)
		k_ctnr_reset(kUintCtn, self->syms);

	k_delete(self->filename);
	self->filename = NULL;
}


//////////////////////////////////////////////////////////////////////////
// 등록

//
void _k_type_red_init(void)
{
	// 문자열
	static const kTypeDecl rsdecl =
	{
		sizeof(kRedStr), KTIF_SEALED,
		_k_redstr_type_init, NULL, NULL, _k_redstr_disp,
	};
	// 와이드 문자열
	static const kTypeDecl rwdecl =
	{
		sizeof(kRedWcs), KTIF_SEALED,
		_k_redwcs_type_init, NULL, NULL, _k_redwcs_disp,
	};
	// 테이블
	static const kTypeDecl rtdecl =
	{
		sizeof(kRedTbl), KTIF_SEALED,
		_k_redtbl_type_init, NULL, _k_redtbl_cnst, _k_redtbl_disp,
	};
	// 함수
	static const kTypeDecl rfdecl =
	{
		sizeof(kRedFn), KTIF_SEALED,
		_k_redfn_type_init, NULL, NULL, _k_redfn_disp,
	};
	// HSM
	// RUN

	//
	k_tmnt_reg_red((kuint)KTYPE_STR, "String", &rsdecl, KTYPE_BASE);
	k_tmnt_reg_red((kuint)KTYPE_WCS, "WideString", &rwdecl, KTYPE_BASE);
	k_tmnt_reg_red((kuint)KTYPE_TBL, "Table", &rtdecl, KTYPE_BASE);
	k_tmnt_reg_red((kuint)KTYPE_FN, "Function", &rfdecl, KTYPE_BASE);

	// 문자열
	k_slice_init(khRsSlice, &kh_red_impl.rs_cache, 128);
	k_mukum_init(khRsMukum, &kh_red_impl.rs_stts);

	// 와이드 문자열
	k_slice_init(khRwSlice, &kh_red_impl.rw_cache, 128);
	k_mukum_init(khRwMukum, &kh_red_impl.rw_stts);

	// 테이블
	k_slice_init(khRtSlice, &kh_red_impl.rt_cache, 64);

	// 함수
	k_slice_init(htRfSlice, &kh_red_impl.rf_cache, 128);
}

//
static void _kh_red_force_unload(kpointer ptr)
{
	k_base_force_unload((kBase*)ptr);
}

//
void _k_type_red_disp(void)
{
	// 문자열
	k_slice_disable(khRsSlice, &kh_red_impl.rs_cache);
	k_slice_ptr_loopeach(khRsSlice, &kh_red_impl.rs_cache, _kh_red_force_unload);
	k_slice_disp(khRsSlice, &kh_red_impl.rs_cache);

	k_mukum_ptr_loopeach_value(khRsMukum, &kh_red_impl.rs_stts, _k_redstr_finalize);
	k_mukum_ptr_disp(khRsMukum, &kh_red_impl.rs_stts);

	// 와이드 문자열
	k_slice_disable(khRwSlice, &kh_red_impl.rw_cache);
	k_slice_ptr_loopeach(khRwSlice, &kh_red_impl.rw_cache, _kh_red_force_unload);
	k_slice_disp(khRwSlice, &kh_red_impl.rw_cache);

	k_mukum_ptr_loopeach_value(khRwMukum, &kh_red_impl.rw_stts, _k_redwcs_finalize);
	k_mukum_ptr_disp(khRwMukum, &kh_red_impl.rw_stts);

	// 테이블
	k_slice_disable(khRtSlice, &kh_red_impl.rt_cache);
	k_slice_ptr_loopeach(khRtSlice, &kh_red_impl.rt_cache, _kh_red_force_unload);
	k_slice_disp(khRtSlice, &kh_red_impl.rt_cache);

	// 함수
	k_slice_disable(htRfSlice, &kh_red_impl.rf_cache);
	k_slice_ptr_loopeach(htRfSlice, &kh_red_impl.rf_cache, _kh_red_force_unload);
	k_slice_disp(htRfSlice, &kh_red_impl.rf_cache);
}
